home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / Other Langs / Tickle-4.0 (tcl) / tcl / src / tclAsync.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-10-25  |  7.2 KB  |  261 lines  |  [TEXT/MPS ]

  1. #ifdef MPW
  2. #    pragma segment TCL_ASYNC
  3. #endif
  4.  
  5. /* 
  6.  * tclAsync.c --
  7.  *
  8.  *    This file provides low-level support needed to invoke signal
  9.  *    handlers in a safe way.  The code here doesn't actually handle
  10.  *    signals, though.  This code is based on proposals made by
  11.  *    Mark Diekhans and Don Libes.
  12.  *
  13.  * Copyright (c) 1993 The Regents of the University of California.
  14.  * All rights reserved.
  15.  *
  16.  * Permission is hereby granted, without written agreement and without
  17.  * license or royalty fees, to use, copy, modify, and distribute this
  18.  * software and its documentation for any purpose, provided that the
  19.  * above copyright notice and the following two paragraphs appear in
  20.  * all copies of this software.
  21.  * 
  22.  * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
  23.  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
  24.  * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
  25.  * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26.  *
  27.  * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
  28.  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
  29.  * AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  30.  * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
  31.  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  32.  */
  33.  
  34. #ifndef lint
  35. static char rcsid[] = "$Header: /user6/ouster/tcl/RCS/tclAsync.c,v 1.3 93/09/02 16:02:42 ouster Exp $ SPRITE (Berkeley)";
  36. #endif /* not lint */
  37.  
  38. #include "tclInt.h"
  39.  
  40. /*
  41.  * One of the following structures exists for each asynchronous
  42.  * handler:
  43.  */
  44.  
  45. typedef struct AsyncHandler {
  46.     int ready;                /* Non-zero means this handler should
  47.                      * be invoked in the next call to
  48.                      * Tcl_AsyncInvoke. */
  49.     struct AsyncHandler *nextPtr;    /* Next in list of all handlers for
  50.                      * the process. */
  51.     Tcl_AsyncProc *proc;        /* Procedure to call when handler
  52.                      * is invoked. */
  53.     ClientData clientData;        /* Value to pass to handler when it
  54.                      * is invoked. */
  55. } AsyncHandler;
  56.  
  57. /*
  58.  * The variables below maintain a list of all existing handlers.
  59.  */
  60.  
  61. static AsyncHandler *firstHandler;    /* First handler defined for process,
  62.                      * or NULL if none. */
  63. static AsyncHandler *lastHandler;    /* Last handler or NULL. */
  64.  
  65. /*
  66.  * The variable below is set to 1 whenever a handler becomes ready and
  67.  * it is cleared to zero whenever Tcl_AsyncInvoke is called.  It can be
  68.  * checked elsewhere in the application to see if Tcl_AsyncInvoke
  69.  * should be invoked.
  70.  */
  71.  
  72. int tcl_AsyncReady = 0;
  73.  
  74. /*
  75.  * The variable below indicates whether Tcl_AsyncInvoke is currently
  76.  * working.  If so then we won't set tcl_AsyncReady again until
  77.  * Tcl_AsyncInvoke returns.
  78.  */
  79.  
  80. static int asyncActive = 0;
  81.  
  82. /*
  83.  *----------------------------------------------------------------------
  84.  *
  85.  * Tcl_AsyncCreate --
  86.  *
  87.  *    This procedure creates the data structures for an asynchronous
  88.  *    handler, so that no memory has to be allocated when the handler
  89.  *    is activated.
  90.  *
  91.  * Results:
  92.  *    The return value is a token for the handler, which can be used
  93.  *    to activate it later on.
  94.  *
  95.  * Side effects:
  96.  *    Information about the handler is recorded.
  97.  *
  98.  *----------------------------------------------------------------------
  99.  */
  100.  
  101. Tcl_AsyncHandler
  102. Tcl_AsyncCreate(proc, clientData)
  103.     Tcl_AsyncProc *proc;        /* Procedure to call when handler
  104.                      * is invoked. */
  105.     ClientData clientData;        /* Argument to pass to handler. */
  106. {
  107.     AsyncHandler *asyncPtr;
  108.  
  109.     asyncPtr = (AsyncHandler *) ckalloc(sizeof(AsyncHandler));
  110.     asyncPtr->ready = 0;
  111.     asyncPtr->nextPtr = NULL;
  112.     asyncPtr->proc = proc;
  113.     asyncPtr->clientData = clientData;
  114.     if (firstHandler == NULL) {
  115.     firstHandler = asyncPtr;
  116.     } else {
  117.     lastHandler->nextPtr = asyncPtr;
  118.     }
  119.     lastHandler = asyncPtr;
  120.     return (Tcl_AsyncHandler) asyncPtr;
  121. }
  122.  
  123. /*
  124.  *----------------------------------------------------------------------
  125.  *
  126.  * Tcl_AsyncMark --
  127.  *
  128.  *    This procedure is called to request that an asynchronous handler
  129.  *    be invoked as soon as possible.  It's typically called from
  130.  *    an interrupt handler, where it isn't safe to do anything that
  131.  *    depends on or modifies application state.
  132.  *
  133.  * Results:
  134.  *    None.
  135.  *
  136.  * Side effects:
  137.  *    The handler gets marked for invocation later.
  138.  *
  139.  *----------------------------------------------------------------------
  140.  */
  141.  
  142. void
  143. Tcl_AsyncMark(async)
  144.     Tcl_AsyncHandler async;        /* Token for handler. */
  145. {
  146.     ((AsyncHandler *) async)->ready = 1;
  147.     if (!asyncActive) {
  148.     tcl_AsyncReady = 1;
  149.     }
  150. }
  151.  
  152. /*
  153.  *----------------------------------------------------------------------
  154.  *
  155.  * Tcl_AsyncInvoke --
  156.  *
  157.  *    This procedure is called at a "safe" time at background level
  158.  *    to invoke any active asynchronous handlers.
  159.  *
  160.  * Results:
  161.  *    The return value is a normal Tcl result, which is intended to
  162.  *    replace the code argument as the current completion code for
  163.  *    interp.
  164.  *
  165.  * Side effects:
  166.  *    Depends on the handlers that are active.
  167.  *
  168.  *----------------------------------------------------------------------
  169.  */
  170.  
  171. int
  172. Tcl_AsyncInvoke(interp, code)
  173.     Tcl_Interp *interp;            /* If invoked from Tcl_Eval just after
  174.                      * completing a command, points to
  175.                      * interpreter.  Otherwise it is
  176.                      * NULL. */
  177.     int code;                 /* If interp is non-NULL, this gives
  178.                      * completion code from command that
  179.                      * just completed. */
  180. {
  181.     AsyncHandler *asyncPtr;
  182.  
  183.     if (tcl_AsyncReady == 0) {
  184.     return code;
  185.     }
  186.     tcl_AsyncReady = 0;
  187.     asyncActive = 1;
  188.     if (interp == NULL) {
  189.     code = 0;
  190.     }
  191.  
  192.     /*
  193.      * Make one or more passes over the list of handlers, invoking
  194.      * at most one handler in each pass.  After invoking a handler,
  195.      * go back to the start of the list again so that (a) if a new
  196.      * higher-priority handler gets marked while executing a lower
  197.      * priority handler, we execute the higher-priority handler
  198.      * next, and (b) if a handler gets deleted during the execution
  199.      * of a handler, then the list structure may change so it isn't
  200.      * safe to continue down the list anyway.
  201.      */
  202.  
  203.     while (1) {
  204.     for (asyncPtr = firstHandler; asyncPtr != NULL;
  205.         asyncPtr = asyncPtr->nextPtr) {
  206.         if (asyncPtr->ready) {
  207.         break;
  208.         }
  209.     }
  210.     if (asyncPtr == NULL) {
  211.         break;
  212.     }
  213.     asyncPtr->ready = 0;
  214.     code = (*asyncPtr->proc)(asyncPtr->clientData, interp, code);
  215.     }
  216.     asyncActive = 0;
  217.     return code;
  218. }
  219.  
  220. /*
  221.  *----------------------------------------------------------------------
  222.  *
  223.  * Tcl_AsyncDelete --
  224.  *
  225.  *    Frees up all the state for an asynchronous handler.  The handler
  226.  *    should never be used again.
  227.  *
  228.  * Results:
  229.  *    None.
  230.  *
  231.  * Side effects:
  232.  *    The state associated with the handler is deleted.
  233.  *
  234.  *----------------------------------------------------------------------
  235.  */
  236.  
  237. void
  238. Tcl_AsyncDelete(async)
  239.     Tcl_AsyncHandler async;        /* Token for handler to delete. */
  240. {
  241.     AsyncHandler *asyncPtr = (AsyncHandler *) async;
  242.     AsyncHandler *prevPtr;
  243.  
  244.     if (firstHandler == asyncPtr) {
  245.     firstHandler = asyncPtr->nextPtr;
  246.     if (firstHandler == NULL) {
  247.         lastHandler = NULL;
  248.     }
  249.     } else {
  250.     prevPtr = firstHandler;
  251.     while (prevPtr->nextPtr != asyncPtr) {
  252.         prevPtr = prevPtr->nextPtr;
  253.     }
  254.     prevPtr->nextPtr = asyncPtr->nextPtr;
  255.     if (lastHandler == asyncPtr) {
  256.         lastHandler = prevPtr;
  257.     }
  258.     }
  259.     ckfree((char *) asyncPtr);
  260. }
  261.